#include "MPEGCompress.hpp"
#include <sstream>
#include <spdlog/spdlog.h>
#include <stdexcept>

static long objectCounter=0;

void pad_added_handler(GstElement* src,GstPad* pad,void* data)
{
  spdlog::info("Receive New Pad: {} ",gst_pad_get_name(pad));
}

MPEGCompress::MPEGCompress()
{
  funcReasultCallback=[](const char*,unsigned long){};
  auto ElementNameFactory=[](){
    return ((std::stringstream())<<"Element"<<objectCounter++).str();
  };
  loop=g_main_loop_new(nullptr,false);
  appsrc=gst_element_factory_make("appsrc",ElementNameFactory().c_str());
  videoconvert=gst_element_factory_make("videoconvertscale",ElementNameFactory().c_str());
  jpegenc=gst_element_factory_make("openjpegenc",ElementNameFactory().c_str());
  jpegparse=gst_element_factory_make("jpeg2000parse",ElementNameFactory().c_str());
  appsink=gst_element_factory_make("appsink",ElementNameFactory().c_str());
  pipeline=gst_pipeline_new(ElementNameFactory().c_str());
  if(!appsrc || !videoconvert || !jpegenc || !jpegparse || !appsink || !pipeline)
    throw std::runtime_error("无法创建Gstreamer元素");
  gst_bin_add_many(GST_BIN(pipeline),appsrc,videoconvert,jpegenc,jpegparse,appsink,nullptr);
  GstCaps* caps=gst_caps_new_simple("video/x-raw",
                                    "format",G_TYPE_STRING,"RGB",
                                    "width",G_TYPE_INT,1920,
                                    "height",G_TYPE_INT,1080,
                                    "framerate",GST_TYPE_FRACTION,50,1,
                                    nullptr
                                    );
  if(!gst_element_link_filtered(appsrc,videoconvert,caps))
    throw std::runtime_error("Can't link appsrc to videoconvert use this caps");
  gst_caps_unref(caps);
  caps=gst_caps_new_simple("video/x-raw",
                           "format",G_TYPE_STRING,"I420",
                           "width",G_TYPE_INT,960,
                           "height",G_TYPE_INT,540,
                           "framerate",GST_TYPE_FRACTION,50,1,
                           nullptr);
  if(!gst_element_link_filtered(videoconvert,jpegenc,caps))
    throw std::runtime_error("Can't link videoconvert to mpeg2enc use this caps");
  gst_caps_unref(caps);
  if(!gst_element_link_many(jpegenc,jpegparse,appsink,nullptr))
    throw std::runtime_error("Can't link gstreamer elements");

  gst_element_set_state(pipeline,GST_STATE_PLAYING);

  mainThread=std::thread([this](){
    g_main_loop_run(loop);
    //播放结束后将播放状态设为空
    gst_element_set_state(pipeline,GST_STATE_NULL);
    //这里是资源回收
    gst_object_unref(GST_OBJECT(pipeline));
    g_main_loop_unref(loop);
    loop=nullptr;

  });
  workThread=std::thread([this](){
    while(true)
    {
      //获取sample，如果没有就会堵塞
      //获得的Sample内部带有互斥锁，是线程安全的
      GstSample* sample = gst_app_sink_pull_sample((GstAppSink*)appsink);
      //如果获得的为空指针，很有可能流被设置为eof
      if(sample==nullptr)
      {
        using namespace std::chrono_literals;
        if(gst_app_sink_is_eos((GstAppSink*)appsink))
          return;
        else
        {
          std::this_thread::sleep_for(1s);
          continue;
        }
      }
      //从sample中获得缓存
      GstBuffer* buffer=gst_sample_get_buffer(sample);
      if(buffer!=nullptr)
      {
        //获取buffer中的mem数量
        guint buffer_num=gst_buffer_n_memory(buffer);
        //遍历提取
        for(int i=0;i<buffer_num;i++)
        {
          //获取图像内存和大小
          GstMemory* mem=gst_buffer_peek_memory(buffer,i);
          gsize size=gst_memory_get_sizes(mem,nullptr,nullptr);
          funcReasultCallback((const char*)mem,size);
        }
      }
      //释放内存，这里不要释放buffer和memory,都是由sample进行管理的
      gst_sample_unref(sample);
    }
  });

}

MPEGCompress::~MPEGCompress()
{
  gst_app_src_end_of_stream((GstAppSrc*)appsrc);
  mainThread.join();
  workThread.join();
}

void MPEGCompress::InputImage(const char* data,unsigned long size)
{
  //申请一个新的缓存
  GstBuffer* buffer=gst_buffer_new();
  //分配内存
  GstMemory* memory=gst_allocator_alloc(nullptr,size,nullptr);
  //获得数据写入的位置
  GstMapInfo info;
  if(!gst_memory_map(memory,&info,GST_MAP_WRITE))
    throw std::runtime_error("Can't get Gstreamer memory");
  memcpy(info.data,data,size);
  gst_memory_unmap(memory,&info);
  //将memory添加到buffer中
  gst_buffer_append_memory(buffer,memory);
  //发送buffer
  if(gst_app_src_push_buffer((GstAppSrc*)appsrc,buffer)!=GST_FLOW_OK)
    spdlog::info("发送图像到压缩管线失败");
}

